/*
 * Copyright 2010 Nicholas Alexander Cross
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.intellij.lang.plist.parser;

import com.intellij.lang.PsiBuilder;
import com.intellij.openapi.util.Key;
import com.intellij.psi.tree.IElementType;

public class ArrayParser {
    final Key<Integer> arrayBraceCounter = new Key<Integer>("array_brace");
    final Key<Boolean> rootOpened = new Key<Boolean>("root_opened");

    private DictionaryParser dictionaryParser;

    public ArrayParser() {
    }

    public void setDictionaryParser(DictionaryParser parser) {
        this.dictionaryParser = parser;
    }

    void checkForInvalidTokens(PsiBuilder builder, IElementType tokenType) {

        if (tokenType == PListElementTypes.RIGHT_PARENTHESES) {
            int i = arrayBraceCount(builder);

            if (i < 0) {
                builder.error("unexpected array closure");
            } else if (!isRootOpened(builder)) {
                builder.error("array closed before it was opened");
            }

            //make not of invalid tokens
            setArrayBraceCounter(builder, i - 1);
        }

    }

    boolean isValueToken(IElementType valueToken) {
        return valueToken == PListElementTypes.STRING
                || valueToken == PListElementTypes.NUMBER
                || valueToken == PListElementTypes.LEFT_PARENTHESES
                || valueToken == PListElementTypes.LEFT_BRACE
                || valueToken == PListElementTypes.DICTIONARY_KEY;
    }

    void parseArray(PsiBuilder builder) {

        setArrayBraceCounter(builder, arrayBraceCount(builder) + 1);

        PsiBuilder.Marker array = builder.mark();

        builder.advanceLexer();

        IElementType valueToken = builder.getTokenType();

        if (isValueToken(valueToken)) {
            parseArrayValue(valueToken, builder, array);
        } else if (valueToken == PListElementTypes.RIGHT_PARENTHESES) {
            closeArray(builder, array);
        } else {
            builder.error("Invalid array");
            array.drop();
        }
    }

    void parseArrayValue(IElementType valueToken, PsiBuilder builder, PsiBuilder.Marker array) {
        if (isValueToken(valueToken)) {

            PsiBuilder.Marker arrayValue = builder.mark();

            if (valueToken == PListElementTypes.STRING || valueToken == PListElementTypes.NUMBER || valueToken == PListElementTypes.DICTIONARY_KEY) {
                builder.advanceLexer();
            } else if (valueToken == PListElementTypes.LEFT_PARENTHESES) {
                parseArray(builder);
            }
            else if (valueToken == PListElementTypes.LEFT_BRACE) {
                dictionaryParser.parseDictionary( builder );
            }
            
            IElementType separatorToken = builder.getTokenType();

            if (separatorToken == PListElementTypes.COMMA) {
                builder.advanceLexer();
                IElementType nextValueToken = builder.getTokenType();
                arrayValue.done(PListElementTypes.ARRAY_VALUE);
                parseArrayValue(nextValueToken, builder, array);
            } else if (separatorToken == PListElementTypes.RIGHT_PARENTHESES) {
                arrayValue.done(PListElementTypes.ARRAY_VALUE);
                closeArray(builder, array);
            } else {
                builder.error("comma delimiter expected");
                arrayValue.drop();
                array.drop();
            }
        } else {
            builder.error("array value expected!");
            array.drop();
        }
    }

    void closeArray(PsiBuilder builder, PsiBuilder.Marker array) {
        final int braceCount = arrayBraceCount(builder) - 1;

        setArrayBraceCounter(builder, braceCount);

        builder.advanceLexer();

        array.done(PListElementTypes.ARRAY);
    }

    void setArrayBraceCounter(PsiBuilder builder, int value) {
        builder.putUserData(
                arrayBraceCounter,
                value
        );
    }

    int arrayBraceCount(PsiBuilder builder) {
        final Integer data = builder.getUserData(arrayBraceCounter);
        return data != null ? data : 0;
    }

    void setRootOpened(PsiBuilder builder, boolean value) {
        builder.putUserData(
                rootOpened,
                value
        );
    }

    boolean isRootOpened(PsiBuilder builder) {
        final Boolean data = builder.getUserData(rootOpened);
        return data != null ? data : false;
    }
}